package cs3343.project.family;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Set;

/**
 * The Class StatisticsController.
 */
public class StatisticsController {
	
	/** The gc. */
	private GameController gc;
	
	/** The game log. */
	private HashMap<Integer, String> gameLog = new HashMap<Integer, String>();
	
	/** The player game log. */
	private LinkedList<HashMap<Integer, String>> playerGameLog = new LinkedList<HashMap<Integer, String>>();
	
	/** The player esteem history. */
	private LinkedList<LinkedList<Integer>> playerEsteemHistory = new LinkedList<LinkedList<Integer>>();
	
	/** The total game time. */
	private int totalGameTime;
	
	/** The total run time. */
	private int totalRunTime;
	
	/** The fastest win time. */
	private int fastestWinTime;
	
	/** The slowest win time. */
	private int slowestWinTime;
	
	/** The max esteem. */
	private int maxEsteem;
	
	/** The min esteem. */
	private int minEsteem;
	
	/** The game time. */
	private int gameTime;
	
	/** The game round. */
	private int gameRound;
	
	/** The esteem. */
	private int esteem;
		
	/**
	 * Instantiates a new statistics controller.
	 *
	 * @param gc the gc
	 */
	public StatisticsController(GameController gc) {
		this.gc = gc;
	}
	
	/**
	 * Gets the game result.
	 *
	 * @return the game result
	 */
	public void getGameResult() {
		gameTime = gc.getRunningTime();
		gameRound = gc.getRound();
		esteem = gc.getMaxEsteem();
	}
	
	/**
	 * Adds the game log.
	 *
	 * @param time the time
	 * @param event the event
	 */
	public void addGameLog(int time, String event) {
		gameLog.put(time, event);
	}
	
	/**
	 * Adds the game log.
	 *
	 * @param event the event
	 */
	public void addGameLog(String event) {
		gameLog.put(gc.getRunningTime(), event);
	}
	
	/**
	 * Addplayer log.
	 *
	 * @param index the index
	 * @param time the time
	 * @param event the event
	 */
	public void addplayerLog(int index, int time, String event) {
		playerGameLog.get(index).put(time, event);
		addGameLog(gc.getCharacterController().getPlayer(index).getName() + event);
	}
	
	/**
	 * Addplayer log.
	 *
	 * @param index the index
	 * @param event the event
	 */
	public void addplayerLog(int index, String event) {
		playerGameLog.get(index).put(gc.getRunningTime(), event);
		addGameLog(gc.getCharacterController().getPlayer(index).getName() + event);
	}
	
	/**
	 * Addplayer log.
	 *
	 * @param player the player
	 * @param event the event
	 */
	public void addplayerLog(Character player, String event) {
		playerGameLog.get(gc.getCharacterController().getPlayerOrder(player)).put(gc.getRunningTime(), event);
		addGameLog(player.getName() + event);
	}
	
	/**
	 * Adds the esteem change.
	 *
	 * @param index the index
	 * @param esteem the esteem
	 */
	public void addEsteemChange(int index, int esteem) {
		playerEsteemHistory.get(index).add(esteem);
	}
	
	/**
	 * Inits the player game log.
	 *
	 * @param numOfPlayer the num of player
	 */
	public void initPlayerGameLog(int numOfPlayer) {
		for (int i = 0; i < numOfPlayer; i++)
			playerGameLog.add(new HashMap<Integer, String>());
	}
	
	/**
	 * Inits the player esteem history.
	 *
	 * @param numOfPlayer the num of player
	 */
	public void initPlayerEsteemHistory(int numOfPlayer) {
		for (int i = 0; i < numOfPlayer; i++)
			playerEsteemHistory.add(new LinkedList<Integer>());
	}

	/**
	 * Gets the player esteem history.
	 *
	 * @param playerIndex the player index
	 * @return the player esteem history
	 */
	public LinkedList<Integer> getPlayerEsteemHistory(int playerIndex) {
		return playerEsteemHistory.get(playerIndex);
	}
	
	/**
	 * Gets the spent time.
	 *
	 * @param runningTime the running time
	 * @return the spent time
	 */
	public Clock getSpentTime(int runningTime) {
		int second = runningTime % 60;
		int minute = runningTime / 60;
		int hour = runningTime / 3600;		
		return new Clock(hour, minute, second);
	}
	
	/**
	 * Load global game stat.
	 */
	public void loadGlobalGameStat() {
		String result[];
		try {
	        BufferedReader infile = new BufferedReader(new InputStreamReader(
	        		new FileInputStream(ConfigManager.GLOBALSTAT_PATH), "UTF-8"));
	        String str;
	        while ((str = infile.readLine()) != null) 
	        {
	        	if (str.length() > 0) {
                    // UTF - 8 BOM CHAR
                    int c = str.charAt(0);
                    if (c == 65279)
                    	str = str.substring(1, str.length());
	        	}
	        	result = str.split(":");

	            if (result[0].equalsIgnoreCase("totalGameTime")) {
	            	try {
	            		totalGameTime = Integer.valueOf(result[1]);
	            	}
	            	catch (Exception e) {
	            		ConfigManager.saveErrorLog(e.toString());
	            	}
	            }
	            else if (result[0].equalsIgnoreCase("totalRunTime")) {
	            	try {
	            		totalRunTime = Integer.parseInt(result[1]);	
	            	}
	            	catch (Exception e) {
	            		ConfigManager.saveErrorLog(e.toString());
	        		}	            	
	            }
	            else if (result[0].equalsIgnoreCase("fastestWinTime")) {
	            	try {
	            		fastestWinTime = Integer.parseInt(result[1]);
	            	}
	            	catch (Exception e) {
	            		ConfigManager.saveErrorLog(e.toString());
	        		}
	            }
	            else if (result[0].equalsIgnoreCase("slowestWinTime")) {
	            	try {
	            		slowestWinTime = Integer.parseInt(result[1]);	
	            	}
	            	catch (Exception e) {
	            		ConfigManager.saveErrorLog(e.toString());
	        		}	            	
	            }
	            else if (result[0].equalsIgnoreCase("maxEsteem")) {
	            	try {
	            		maxEsteem = Integer.parseInt(result[1]);
	            	}
	            	catch (Exception e) {
	            		ConfigManager.saveErrorLog(e.toString());
	        		}
	            }
	            else if (result[0].equalsIgnoreCase("minEsteem")) {
	            	try {
	            		minEsteem = Integer.parseInt(result[1]);
	            	}
	            	catch (Exception e) {
	            		ConfigManager.saveErrorLog(e.toString());
	        		}
	            }
	            else
	            	System.out.println("Error");
	        }
	        infile.close();
	    } 
	    catch (IOException e) {
	    	ConfigManager.saveErrorLog(e.toString());
	    }
	}
	
	/**
	 * Save global game stat.
	 *
	 * @return true, if successful
	 */
	public void saveGlobalGameStat() {
		try	{
			BufferedWriter outfile = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(ConfigManager.GLOBALSTAT_PATH, false), "UTF-8"));
			totalGameTime += gameTime;
			outfile.append("totalGameTime:" + totalGameTime);
			outfile.newLine();
			outfile.append("totalRunTime:" + ++totalRunTime);
			outfile.newLine();
			if (isGreater(gameTime, fastestWinTime))
				outfile.append("fastestWinTime:" + gameTime);
			else
				outfile.append("fastestWinTime:" + fastestWinTime);
			outfile.newLine();
			if (isGreater(gameTime, slowestWinTime))
				outfile.append("slowestWinTime:" + slowestWinTime);
			else
				outfile.append("slowestWinTime:" + gameTime);
			outfile.newLine();
			if (isGreater(esteem, maxEsteem))
				outfile.append("maxEsteem:" + esteem);
			else
				outfile.append("maxEsteem:" + maxEsteem);
			outfile.newLine();
			if (isGreater(esteem, minEsteem))
				outfile.append("minEsteem:" + minEsteem);
			else
				outfile.append("minEsteem:" + esteem);
			outfile.newLine();
			outfile.close();
		}
		catch (IOException e) {
			ConfigManager.saveErrorLog(e.toString());
		}
	}
	
	/**
	 * Checks if is greater.
	 *
	 * @param newValue the new value
	 * @param oldValue the old value
	 * @return true, if is greater
	 */
	public boolean isGreater(int newValue, int oldValue) {
		if (newValue > oldValue)
			return true;
		return false;		
	}
	
	/**
	 * Save last game stat.
	 */
	public void saveLastGameStat() {
		try	{
			BufferedWriter outfile = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(ConfigManager.LASTSTAT_PATH, false), "UTF-8"));
			outfile.append("lastGametime:" + gameTime);
			outfile.newLine();
			outfile.append("lastGameRound:" + gameRound);
			outfile.newLine();
			outfile.append("lastEsteem:" + esteem);
			outfile.newLine();
			outfile.close();
		}
		catch (IOException e) {
			ConfigManager.saveErrorLog(e.toString());
		}		
	}
	
	/**
	 * Load last game stat.
	 */
	public void loadLastGameStat() {
		String result[];
		try {
	        BufferedReader infile = new BufferedReader(new InputStreamReader(
	        		new FileInputStream(ConfigManager.LASTSTAT_PATH), "UTF-8"));
	        String str;
	        while ((str = infile.readLine()) != null) 
	        {
	        	if (str.length() > 0) {
                    // UTF - 8 BOM CHAR
                    int c = str.charAt(0);
                    if (c == 65279)
                    	str = str.substring(1, str.length());
	        	}
	        	result = str.split(":");

	            if (result[0].equalsIgnoreCase("lastGametime")) {
	            	try {
	            		gameTime = Integer.valueOf(result[1]);
	            	}
	            	catch (Exception e) {
	            		ConfigManager.saveErrorLog(e.toString());
	            	}
	            }
	            else if (result[0].equalsIgnoreCase("totalRunTime")) {
	            	try {
	            		totalRunTime = Integer.parseInt(result[1]);	
	            	}
	            	catch (Exception e) {
	            		ConfigManager.saveErrorLog(e.toString());
	        		}	            	
	            }
	            else if (result[0].equalsIgnoreCase("lastGameRound")) {
	            	try {
	            		gameRound = Integer.parseInt(result[1]);
	            	}
	            	catch (Exception e) {
	            		ConfigManager.saveErrorLog(e.toString());
	        		}
	            }
	            else if (result[0].equalsIgnoreCase("lastEsteem")) {
	            	try {
	            		esteem = Integer.parseInt(result[1]);	
	            	}
	            	catch (Exception e) {
	            		ConfigManager.saveErrorLog(e.toString());
	        		}	            	
	            }
	            else
	            	System.out.println("Error");
	        }
	        infile.close();
	    } 
	    catch (IOException e) {
	    	ConfigManager.saveErrorLog(e.toString());
	    }
	}
	
	/**
	 * Generate game log file.
	 *
	 * @param folder the folder
	 */
	public void generateGameLogFile(String folder) {
		Set<Integer> keyset = gameLog.keySet();
		String fileName = folder + "game.txt";
		
		try	{
			BufferedWriter outfile = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(fileName, false), "UTF-8"));
			for (Integer key : keyset) {
				outfile.append("在第 " + key + " 秒： " + gameLog.get(key));
				outfile.newLine();
			}
			outfile.close();
		}
		catch (IOException e) {
			ConfigManager.saveErrorLog(e.toString());
		}
	}
	
	/**
	 * Generate player log file.
	 *
	 * @param folder the folder
	 */
	public void generatePlayerLogFile(String folder) {
		String fileName = folder;
		
		try	{
			BufferedWriter outfile;
			for (int i = 0; i < playerGameLog.size(); i++) {
				outfile = new BufferedWriter(new OutputStreamWriter(
						new FileOutputStream(fileName + "/player" + (i + 1) + ".txt", false), "UTF-8"));
				Set<Integer> keyset = playerGameLog.get(i).keySet();
				for (Integer key : keyset) {
					outfile.append("在第 " + key + " 秒： " + playerGameLog.get(i).get(key));
					outfile.newLine();
				}
				outfile.close();
			}
		}
		catch (IOException e) {
			ConfigManager.saveErrorLog(e.toString());
		}
	}

	/**
	 * Gets the game log.
	 *
	 * @return the game log
	 */
	public HashMap<Integer, String> getGameLog() {
		return gameLog;
	}

	/**
	 * Sets the game log.
	 *
	 * @param gameLog the game log
	 */
	public void setGameLog(HashMap<Integer, String> gameLog) {
		this.gameLog = gameLog;
	}

	/**
	 * Gets the player game log.
	 *
	 * @return the player game log
	 */
	public LinkedList<HashMap<Integer, String>> getPlayerGameLog() {
		return playerGameLog;
	}

	/**
	 * Sets the player game log.
	 *
	 * @param playerGameLog the player game log
	 */
	public void setPlayerGameLog(LinkedList<HashMap<Integer, String>> playerGameLog) {
		this.playerGameLog = playerGameLog;
	}

	/**
	 * Gets the player esteem history.
	 *
	 * @return the player esteem history
	 */
	public LinkedList<LinkedList<Integer>> getPlayerEsteemHistory() {
		return playerEsteemHistory;
	}

	/**
	 * Sets the player esteem history.
	 *
	 * @param playerEsteemHistory the new player esteem history
	 */
	public void setPlayerEsteemHistory(
			LinkedList<LinkedList<Integer>> playerEsteemHistory) {
		this.playerEsteemHistory = playerEsteemHistory;
	}

	/**
	 * Gets the total game time.
	 *
	 * @return the total game time
	 */
	public int getTotalGameTime() {
		return totalGameTime;
	}

	/**
	 * Sets the total game time.
	 *
	 * @param totalGameTime the new total game time
	 */
	public void setTotalGameTime(int totalGameTime) {
		this.totalGameTime = totalGameTime;
	}

	/**
	 * Gets the total run time.
	 *
	 * @return the total run time
	 */
	public int getTotalRunTime() {
		return totalRunTime;
	}

	/**
	 * Sets the total run time.
	 *
	 * @param totalRunTime the new total run time
	 */
	public void setTotalRunTime(int totalRunTime) {
		this.totalRunTime = totalRunTime;
	}

	/**
	 * Gets the fastest win time.
	 *
	 * @return the fastest win time
	 */
	public int getFastestWinTime() {
		return fastestWinTime;
	}

	/**
	 * Sets the fastest win time.
	 *
	 * @param fastestWinTime the new fastest win time
	 */
	public void setFastestWinTime(int fastestWinTime) {
		this.fastestWinTime = fastestWinTime;
	}

	/**
	 * Gets the slowest win time.
	 *
	 * @return the slowest win time
	 */
	public int getSlowestWinTime() {
		return slowestWinTime;
	}

	/**
	 * Sets the slowest win time.
	 *
	 * @param slowestWinTime the new slowest win time
	 */
	public void setSlowestWinTime(int slowestWinTime) {
		this.slowestWinTime = slowestWinTime;
	}

	/**
	 * Gets the max esteem.
	 *
	 * @return the max esteem
	 */
	public int getMaxEsteem() {
		return maxEsteem;
	}

	/**
	 * Sets the max esteem.
	 *
	 * @param maxEsteem the new max esteem
	 */
	public void setMaxEsteem(int maxEsteem) {
		this.maxEsteem = maxEsteem;
	}

	/**
	 * Gets the min esteem.
	 *
	 * @return the min esteem
	 */
	public int getMinEsteem() {
		return minEsteem;
	}

	/**
	 * Sets the min esteem.
	 *
	 * @param minEsteem the new min esteem
	 */
	public void setMinEsteem(int minEsteem) {
		this.minEsteem = minEsteem;
	}

	/**
	 * Gets the game time.
	 *
	 * @return the game time
	 */
	public int getGameTime() {
		return gameTime;
	}

	/**
	 * Sets the game time.
	 *
	 * @param gameTime the new game time
	 */
	public void setGameTime(int gameTime) {
		this.gameTime = gameTime;
	}

	/**
	 * Gets the game round.
	 *
	 * @return the game round
	 */
	public int getGameRound() {
		return gameRound;
	}

	/**
	 * Sets the game round.
	 *
	 * @param gameRound the new game round
	 */
	public void setGameRound(int gameRound) {
		this.gameRound = gameRound;
	}

	/**
	 * Gets the esteem.
	 *
	 * @return the esteem
	 */
	public int getEsteem() {
		return esteem;
	}

	/**
	 * Sets the esteem.
	 *
	 * @param esteem the new esteem
	 */
	public void setEsteem(int esteem) {
		this.esteem = esteem;
	}
}
